//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/image.h>
#include <buola/xml.h>
#include <buola/io/curi.h>

#include <buola/image/cfont.h>
#include <buola/image/cgc.h>
#include <buola/image/cfontconfig.h>
#include <buola/image/ctextlayout.h>
#include <buola/io/cresourcefinder.h>
#include <buola/image/font/cscaledfont.h>

namespace buola { namespace img {

struct CFont::SFont
{
    FcCharSet *mCharSet;
    FcPattern *mPattern;
    CFontFace *mFace;
    CScaledFont *mScaled;
};
    
std::unordered_map<std::wstring,PFont> CFont::mStockFonts;
std::unordered_map<CFontInfo,PFont> CFont::mAllFonts;
recursive_mutex CFont::mStaticMutex;

CFont::CFont(const CFontInfo &pInfo)
{
    mInfo=pInfo;
    mInfo.SetDPI(96);

    FcResult lResult;

    FcPattern *lPattern=FcPatternDuplicate(CFontConfig::GetPattern(mInfo));
    FcFontSet *lSet=FcFontSort(nullptr,lPattern,FcTrue,nullptr,&lResult);

    for(int i=0;i<lSet->nfont;i++)
    {
        FcPattern *lThis=FcFontRenderPrepare(nullptr,lPattern,lSet->fonts[i]);
        //if(i==0)
        //    FcPatternPrint(lThis);

        if(lThis)
        {
            SFont lFont;
            lFont.mPattern=lThis;
            lFont.mCharSet=nullptr;
            lFont.mFace=nullptr;
            lFont.mScaled=nullptr;
            mFonts.push_back(lFont);
        }
    }

    FcPatternDestroy(lPattern);
    FcFontSetDestroy(lSet);

    CalcMetrics();
}

CFont::~CFont()
{
}

double CFont::GetWidth(const std::wstring &pStr)
{
    CTextLayout lLayout(pStr);
    lLayout.SetDefaultFont(this);
    return lLayout.GetSize().x;
}

PFont CFont::Get(const CFontInfo &pInfo)
{
    lock_guard<recursive_mutex> lLock(mStaticMutex);

    PFont lFont=mAllFonts[pInfo];

    if(lFont)
    {
        return lFont;
    }

    lFont=new CFont(pInfo);

    mAllFonts[pInfo]=lFont;
    return lFont;
}

PFont CFont::GetStock(const std::wstring &pName)
{
    lock_guard<recursive_mutex> lLock(mStaticMutex);

    PFont lFont=mStockFonts[pName];

    if(lFont)
    {
        return lFont;
    }

    xml::CDoc lDoc;
    lDoc.Load(io::data_finder().FindFile("buola/registry/fonts.xml"));

    for(auto lChild : lDoc.Root().Children())
    {
        if(lChild.Name()=="font")
        {
            std::wstring lThis=utf32(lChild.Content());

            if(lThis==pName)
            {
                CFontInfo lInfo(lChild);
                lFont=Get(lInfo);
                mStockFonts[pName]=lFont;
                break;
            }
        }
    }

    if(!lFont)
    {
        throw XNotFound(I18N("Stock font not found"));
    }

    return lFont;
}

void CFont::GetFamilyList(std::vector<std::string> &pList)
{
    CFontConfig::Init();

    FcObjectSet *lObjectSet=FcObjectSetCreate();
    FcObjectSetAdd(lObjectSet,FC_FAMILY);
    FcPattern *lPattern=FcPatternCreate();
    FcFontSet *lSet=FcFontList(nullptr,lPattern,lObjectSet);

    pList.clear();

    for(int i=0;i<lSet->nfont;i++)
    {
        FcChar8 *lString;
        FcPatternGetString(lSet->fonts[i],FC_FAMILY,0,&lString);
        pList.push_back((const char*)lString);
    }

    FcFontSetDestroy(lSet);
    FcObjectSetDestroy(lObjectSet);
    FcPatternDestroy(lPattern);
}

void CFont::GetStyleList(const std::string &pFamily,std::vector<std::string> &pList)
{
    CFontConfig::Init();

    FcObjectSet *lObjectSet=FcObjectSetCreate();
    FcObjectSetAdd(lObjectSet,FC_STYLE);
    FcPattern *lPattern=FcPatternCreate();
    FcPatternAddString(lPattern,FC_FAMILY,(const FcChar8*)pFamily.c_str());
    FcFontSet *lSet=FcFontList(nullptr,lPattern,lObjectSet);

    pList.clear();

    for(int i=0;i<lSet->nfont;i++)
    {
        FcChar8 *lString;
        FcPatternGetString(lSet->fonts[i],FC_STYLE,0,&lString);
        pList.push_back((const char*)lString);
    }

    FcFontSetDestroy(lSet);
    FcObjectSetDestroy(lObjectSet);
    FcPatternDestroy(lPattern);
}

int CFont::GetNumGlyphs(TFontIndex pF)
{
    return GetFontFace(pF)->GetNumGlyphs();
}

bool CFont::GetFontTable(TFontIndex pF,uint32_t pTag,void *pDst,unsigned int &pLen)
{
    return GetFontFace(pF)->GetFontTable(pTag,pDst,pLen);
}

TFontIndex CFont::GetSubFont(wchar_t pC)
{
    for(int i=0;i<mFonts.size();i++)
    {
        if(!mFonts[i].mCharSet)
        {
            if(FcPatternGetCharSet(mFonts[i].mPattern,FC_CHARSET,0,&mFonts[i].mCharSet)!=FcResultMatch)
            {
                msg_warn() << "can't get charset\n";
                continue;
            }
        }

        if(FcCharSetHasChar(mFonts[i].mCharSet,pC))
            return i;
    }

    return -1;
}

bool CFont::HasChar(wchar_t pC,TFontIndex pI)
{
    if(!mFonts[pI].mCharSet)
    {
        if(FcPatternGetCharSet(mFonts[pI].mPattern,FC_CHARSET,0,
                    &mFonts[pI].mCharSet)!=FcResultMatch)
        {
            msg_warn() << "can't get charset\n";
            return false;
        }
    }

    return FcCharSetHasChar(mFonts[pI].mCharSet,pC);
}

TGlyphIndex CFont::GetGlyph(wchar_t pC,TFontIndex pFont)
{
    return GetFontFace(pFont)->GetGlyph(pC);
}

void CFont::FillGlyphInfo(CGlyphInfo &pG)
{
    CScaledFont *lScaled=GetScaledFont(pG.mFont);

    lScaled->FillGlyphInfo(pG);
}

int CFont::GetAdvance(TFontIndex pFont,TGlyphIndex pGlyph)
{
    CGlyphInfo lG;
    lG.mFont=pFont;
    lG.mGlyph=pGlyph;

    FillGlyphInfo(lG);

    return lG.mXOff;
}

int CFont::GetUnitsPerEM(TFontIndex pFont)
{
    return GetFontFace(pFont)->GetUnitsPerEM();
}

CFontFace *CFont::GetFontFace(int i)
{
    if(!mFonts[i].mFace)
    {
        mFonts[i].mFace=CFontFace::GetForPattern(mFonts[i].mPattern);
    }

    return mFonts[i].mFace;
}

CScaledFont *CFont::GetScaledFont(int i)
{
    if(!mFonts[i].mScaled)
    {
        double lScale;
        FcPatternGetDouble(mFonts[i].mPattern,FC_PIXEL_SIZE,0,&lScale);
        mat::C2DTransform lFM;
        lFM.Scale(lScale);
        mat::C2DTransform lTM;

        mFonts[i].mScaled=CScaledFont::Create(GetFontFace(i),lFM,lTM);
    }

    return mFonts[i].mScaled;
}

void CFont::CalcMetrics()
{
    FcPatternGetDouble(mFonts[0].mPattern,FC_SIZE,0,&mMetrics.mPointSize);
    FcPatternGetDouble(mFonts[0].mPattern,FC_PIXEL_SIZE,0,&mMetrics.mPixelSize);

    mMetrics=GetScaledFont(0)->GetMetrics();
}

CLayoutEngine *CFont::GetEngine(const CTextLayout::SAnalysis &pAnalysis)
{
    CLayoutEngine* &lE=mEngines[std::make_pair(pAnalysis.mScript,pAnalysis.mFont)];

    if(!lE)
    {
        lE=CLayoutEngine::GetEngine(this,pAnalysis);
    }

    return lE;
}


#if 0
///\todo move this to copenglgc and fix it
void CFont::DrawGlyphs(int *pGlyphs,CPoint_d *pPositions,int pN,
            TFontIndex pIndex,CGC *pGC)
{
    throw XNotImplemented("second version of draw glyphs");
#if 0
    if(pIndex<0) return;

    cairo_t *lCR;

    if(CCairoGC *lGC=dynamic_cast<CCairoGC*>(pGC))
    {
        lCR=lGC->GetCairoContext();
    }
    else
    {
        throw XInvalid("wrong gc type");
    }

    cairo_glyph_t lGlyphs[pN];

    cairo_set_scaled_font(lCR,GetCairo(pIndex));

    for(int i=0;i<pN;i++)
    {
        lGlyphs[i].index=pGlyphs[i];
        lGlyphs[i].x=pPositions[i].x;
        lGlyphs[i].y=pPositions[i].y;
    }

    cairo_show_glyphs(lCR,lGlyphs,pN);
#endif
}
#endif

/*namespace img*/ } /*namespace buola*/ }
